/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define APK_VERSION	"V1.3"
#define LOG_TAG "HdmiInput-navtive"
//#define LOG_NDEBUG 0

#include <utils/Log.h>
#include "jni.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <utils/Log.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <linux/v4l2-subdev.h>
#include <cutils/properties.h>
#include <dirent.h> 

#define LOGE(msg,...)   ALOGE("%s(%d): " msg ,__FUNCTION__,__LINE__,##__VA_ARGS__)
#define LOGD(msg,...)   ALOGD("%s(%d): " msg ,__FUNCTION__,__LINE__,##__VA_ARGS__)
#define LOGV(msg,...)   ALOGV("%s(%d): " msg ,__FUNCTION__,__LINE__,##__VA_ARGS__)

#define V4L2_CID_USER_RK_BASE             (V4L2_CID_USER_BASE + 0x1080)
#define RK_V4L2_CID_AUDIO_SAMPLING_RATE (V4L2_CID_USER_RK_BASE + 0x100)

#define BASE_VIDIOC_PRIVATE 192     /* 192-255 are private */
#define RKMODULE_GET_HDMI_MODE       \
        _IOR('V', BASE_VIDIOC_PRIVATE + 34, __u32)

#define UNUSED(x) (void)x 

const int kMaxDevicePathLen = 256;
const char* kDevicePath = "/dev/";
const char kPrefix[] = "v4l-subdev";
const int kPrefixLen = sizeof(kPrefix) - 1;
char kV4l2DevicePath[kMaxDevicePathLen];
int mMipiHdmi = -1;

static int getAudioRate(JNIEnv *env, jobject thiz)
{
	UNUSED(env);
	UNUSED(thiz);
	
    struct v4l2_control control;
	int err;
	
	if (mMipiHdmi > 0) {
		memset(&control, 0, sizeof(struct v4l2_control));
		control.id = V4L2_CID_DV_RX_POWER_PRESENT;
		err = ioctl(mMipiHdmi, VIDIOC_G_CTRL, &control);
		if (err < 0) {
			LOGV("Set POWER_PRESENT failed ,%d(%s)", errno, strerror(errno));
			return 0;
		}
		
		if (control.value) {		
			memset(&control, 0, sizeof(struct v4l2_control));
			control.id = RK_V4L2_CID_AUDIO_SAMPLING_RATE;
			sleep(1);
			err = ioctl(mMipiHdmi, VIDIOC_G_CTRL, &control);
			if (err < 0) {
				LOGE("get audio sampling rate failed ,%d(%s)", errno, strerror(errno));
				return 0;
			}
		
			int rate = control.value;

			//LOGD("audio rate: %d", rate);

			char value[PROPERTY_VALUE_MAX] = "";
			sprintf(value, "%d", rate);
			property_set("vendor.hdmiin.audiorate", value);
			
			return rate;
		}
	}
	
	return 0;
}

static void getDeviceFormat(int *format)
{
    struct v4l2_control control;
	//struct v4l2_dv_timings dv_timings;
	struct v4l2_subdev_format aFormat;
	
	if (mMipiHdmi > 0) {
		memset(&control, 0, sizeof(struct v4l2_control));
		control.id = V4L2_CID_DV_RX_POWER_PRESENT;
		int err = ioctl(mMipiHdmi, VIDIOC_G_CTRL, &control);
		if (err < 0) {
			LOGV("Set POWER_PRESENT failed ,%d(%s)", errno, strerror(errno));
			control.value = 0;
		}
		
		format[2] = control.value;

		if (control.value) {		
			/*memset(&dv_timings, 0 ,sizeof(struct v4l2_dv_timings));
			err = ioctl(mMipiHdmi, VIDIOC_SUBDEV_QUERY_DV_TIMINGS, &dv_timings);
			if (err < 0) {
				LOGV("Set VIDIOC_SUBDEV_QUERY_DV_TIMINGS failed ,%d(%s)", errno, strerror(errno));
				format[0] = 0;
				format[1] = 0;
			} else {
				format[0] = dv_timings.bt.width;
				format[1] = dv_timings.bt.height;
			}*/			
			int err = ioctl(mMipiHdmi, VIDIOC_SUBDEV_G_FMT, &aFormat);
			if (err < 0) {
				LOGV("Set VIDIOC_SUBDEV_G_FMT failed ,%d(%s)", errno, strerror(errno));
				format[0] = 0;
				format[1] = 0;
			} else {
				format[0] = aFormat.format.width;
				format[1] = aFormat.format.height;
				
				/*ALOGD("VIDIOC_SUBDEV_G_FMT: pad: %d, which: %d, width: %d, "
				"height: %d, format: 0x%x, field: %d, color space: %d",
				aFormat.pad,
				aFormat.which,
				aFormat.format.width,
				aFormat.format.height,
				aFormat.format.code,
				aFormat.format.field,
				aFormat.format.colorspace);*/
			}			
		} else {
			format[0] = 0;
			format[1] = 0;
		}    
	} else {
		format[0] = 0;
		format[1] = 0;
		format[2] = 0;
	}
	
	//LOGE("Lyle,Hdmi in:%d,%d,%d",format[0],format[1],format[2]);
}

static jintArray getFormat(JNIEnv *env, jobject thiz)
{
    (void)thiz;
    jintArray array = env->NewIntArray(3);
    jint *result = new jint[3];
    getDeviceFormat(result);
    env->SetIntArrayRegion(array, 0, 3, result);
    delete[] result;
    return array;
}

static void openDevice(JNIEnv *env, jobject thiz)
{
    (void)*env;
    (void)thiz;
	
	DIR* devdir = opendir(kDevicePath);
    if(devdir == 0) {
        ALOGE("%s: cannot open %s! ", __FUNCTION__, kDevicePath);
        return;
    }
    struct dirent* de;
    int videofd,ret;
    while ((de = readdir(devdir)) != 0) {
        // Find external v4l devices that's existing before we start watching and add them
        if (!strncmp(kPrefix, de->d_name, kPrefixLen)) {
            //std::string deviceId(de->d_name + kPrefixLen);
            ALOGD("found %s", de->d_name);
            //char v4l2DeviceDriver[16];
            snprintf(kV4l2DevicePath, kMaxDevicePathLen,"%s%s", kDevicePath, de->d_name);
            videofd = open(kV4l2DevicePath, O_RDWR);
            if (videofd < 0){
                ALOGE("[%s %d] open device failed:%x [%s]", __FUNCTION__, __LINE__, videofd,strerror(errno));
                continue;
            } else {
                uint32_t ishdmi;
                ret = ::ioctl(videofd, RKMODULE_GET_HDMI_MODE, (void*)&ishdmi);
                if (ret < 0) {
                    ALOGE("RKMODULE_GET_HDMI_MODE Failed, error: %s", strerror(errno));
                    close(videofd);
                    continue;
                }
                ALOGD("%s RKMODULE_GET_HDMI_MODE:%d",kV4l2DevicePath,ishdmi);
                if (ishdmi)
                {
                    mMipiHdmi = videofd;
                    ALOGD("MipiHdmi fd:%d",mMipiHdmi);
                    if (mMipiHdmi < 0)
                    {
                        //return ret;
                    }
                }
            }
        }
    }
    closedir(devdir);	
}

static void closeDevice(JNIEnv *env, jobject thiz)
{
    (void)*env;
    (void)thiz;
    LOGD("close device");
	if(mMipiHdmi > 0) {
        close(mMipiHdmi);
		mMipiHdmi = -1;
    }
}

static const char *classPathName = "com/android/rockchip/camera2/util/JniCameraCall";

static JNINativeMethod methods[] = {
    {"getFormat", "()[I", (void *)getFormat},
    {"openDevice", "()V", (void *)openDevice},
    {"closeDevice", "()V", (void *)closeDevice},
	{"getAudioRate", "()I", (void *)getAudioRate},
};

/*
 * Register several native methods for one class.
 */
static int registerNativeMethods(JNIEnv *env, const char *className,
                                JNINativeMethod *gMethods, int numMethods)
{
    jclass clazz;

    clazz = env->FindClass(className);
    if (clazz == NULL)
    {
        ALOGE("Native registration unable to find class '%s'", className);
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0)
    {
        ALOGE("RegisterNatives failed for '%s'", className);
        return JNI_FALSE;
    }

    return JNI_TRUE;
}

/*
 * Register native methods for all classes we know about.
 *
 * returns JNI_TRUE on success.
 */
static int registerNatives(JNIEnv *env)
{
    if (!registerNativeMethods(env, classPathName,
            methods, sizeof(methods) / sizeof(methods[0])))
    {
        return JNI_FALSE;
    }

    return JNI_TRUE;
}

// ----------------------------------------------------------------------------

/*
 * This is called by the VM when the shared library is first loaded.
 */

typedef union {
    JNIEnv *env;
    void *venv;
} UnionJNIEnvToVoid;

jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
    (void)reserved;
    UnionJNIEnvToVoid uenv;
    uenv.venv = NULL;
    jint result = -1;
    JNIEnv *env = NULL;

    ALOGI("JNI_OnLoad");
    ALOGI("Apk Version: %s", APK_VERSION);

    if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK)
    {
        ALOGE("ERROR: GetEnv failed");
        goto bail;
    }
    env = uenv.env;

    if (registerNatives(env) != JNI_TRUE)
    {
        ALOGE("ERROR: registerNatives failed");
        goto bail;
    }

    result = JNI_VERSION_1_4;

bail:
    return result;
}

